package inno.main;


import inno.specificCrossover.Crossover;
import inno.specificFitness.Fitness;
import inno.specificMutaion.Mutation;
import inno.specificSelection.Selection;
import inno.treeRepro.Chromosome;
import inno.treeRepro.ChromosomeComparator;

import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;

public class World {
	
	private HashMap<String,Chromosome> stocksChromosomes;
	
	private Crossover myCrossover;
	private Fitness myFitness;
	private Mutation myMutation;
	private Selection mySelection;
	private int eliteSize;
	private int populationSize;
	private int generations;
	
	int stocksSum;
	String stocksNames[];
	
	public World( int stocksSum, String stocksNames[], Crossover myCrossover,
			Fitness myFitness, Mutation myMutation, Selection mySelection,int eliteSize,
			int populationSize, int generations){

		this.myCrossover = myCrossover;
		this.myFitness = myFitness;
		this.myMutation = myMutation;
		this.mySelection = mySelection;
		this.eliteSize = eliteSize;
		this.populationSize = populationSize;
		this.stocksSum = stocksSum;
		this.stocksNames = stocksNames;
		this.generations = generations;
		stocksChromosomes = new HashMap<String, Chromosome>();
	}
	
	public void Play(String dataFolder, String rulesFileName){
		ChromosomeComparator myComparator = new ChromosomeComparator();
		// do the game for each stock
		for (int k=0; k<stocksSum;k++){
			// initialize
			ArrayList<Chromosome> population = new ArrayList<Chromosome>();
			for (int i=0; i<populationSize;i++){
				population.add(new Chromosome(stocksNames[k]));
			}
			// and start rolling
			
			for (int l=0; l<generations; l++){
				ArrayList<Chromosome> nextPopulation = new ArrayList<Chromosome>();
				
				// calculate the fitnes
				for (int i=0; i<populationSize;i++){
				//	System.out.println(population.get(i).toString());
					population.get(i).setFitness(myFitness.doFitness(population.get(i)));
				}
				
				// save the elite
				//Collections.sort(population, myComparator);
				//Collections.sort(population);
				Collections.sort(population, new Comparator<Chromosome>() {
					@Override
				    public int compare(Chromosome o1, Chromosome o2) {
						Double dif = o1.getMyFitness()-o2.getMyFitness();
						if (dif<0.0){
							return 1;
						}
						else if (dif>0.0){
							return -1;
						}
						else{
							return 0;
						}
						// sides replaced because i want the bigget to be in the high indexes
				    }
				});
				for (int i=0; i<eliteSize;i++){
					nextPopulation.add(new Chromosome(population.get(i)));
				}
				
				// do selection
				//for (int i=0; i< ((populationSize-eliteSize)/2);i++){
				for (int i=0; i< (populationSize-eliteSize);i++){
					ArrayList<Chromosome> selected = mySelection.doSelection(population);
					Random rand = new Random();
					int randomNum = rand.nextInt(100);
					// crossover in 74 rate
					if (randomNum<74){
						ArrayList<Chromosome> crossovered = myCrossover.doCrossover(selected.get(0), selected.get(1));
						nextPopulation.add(new Chromosome(crossovered.get(0)));
						//nextPopulation.add(new Chromosome(crossovered.get(1)));	
					}
					else{
						nextPopulation.add(new Chromosome(selected.get(0)));
						//nextPopulation.add(new Chromosome(selected.get(1)));
					}				
				}
				
				population.clear();
				population = nextPopulation;
				// mutation
				for (int i=0; i<populationSize;i++){
					if (i==population.size()){
						int a = 5;
					}
					myMutation.doMutation(population.get(i));
				}
			}
			
			// calculate the fitnes
			for (int i=0; i<populationSize;i++){
				population.get(i).setFitness(myFitness.doFitness(population.get(i)));
			}
			//Collections.sort(population, myComparator);
			Collections.sort(population, new Comparator<Chromosome>() {
				@Override
			    public int compare(Chromosome o1, Chromosome o2) {
					Double dif = o1.getMyFitness()-o2.getMyFitness();
					if (dif<0.0){
						return 1;
					}
					else if (dif>0.0){
						return -1;
					}
					else{
						return 0;
					}
					// sides replaced because i want the bigget to be in the high indexes
			    }
			});
			
			// save the best chromosome for the current stock
			stocksChromosomes.put(stocksNames[k], population.get(0));
		}	
		// print the chromosome we got
		try {
			FileWriter fstream = new FileWriter(dataFolder+'/'+rulesFileName+".txt");
			 BufferedWriter out = new BufferedWriter(fstream);
			 for (int i=0;i<stocksSum;i++){
					//System.out.println(stocksChromosomes.get(stocksNames[i]).toString());
					out.write(stocksChromosomes.get(stocksNames[i]).toString()+'\n');
				}
			 out.close();
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
	}
	
	public Chromosome getStockChromosome(String stockName){
		return stocksChromosomes.get(stockName);
	}
}
